﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Transactions;
using Bricks.Data;

namespace Bricks.EntityFramework.Data
{

    /// <summary>
    /// 
    /// </summary>
    public class DbContextFacade : ContextFacadeBase<DbContext>, IDbContextContextFacade
    {

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dbContext"></param>
        public DbContextFacade(DbContext dbContext) :
            base(dbContext)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="dbContext"></param>
        /// <returns></returns>
        protected virtual IDbSet<TEntity> Set<TEntity>(DbContext dbContext) where TEntity : class
        {
            return dbContext.Set<TEntity>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="action"></param>
        /// <returns></returns>
        public override TResult Transact<TResult>(Func<TResult> action)
        {
            return Execute<TResult>(dbContext =>
            {
                using (TransactionScope transaction = new TransactionScope())
                {
                    TResult result = action.Invoke();

                    dbContext.SaveChanges();

                    transaction.Complete();

                    return result;
                }
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public virtual int Count<TEntity>(Func<IQueryable<TEntity>, IQueryable<TEntity>> predicate) where TEntity : class
        {
            return Execute<int>(dbContext => predicate.Invoke(Set<TEntity>(dbContext)).Count());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public override int Count<TEntity>(Expression<Func<TEntity, bool>> predicate)
        {
            return Count<TEntity>(query => query.Where(predicate));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        public override int CountAll<TEntity>()
        {
            return Count<TEntity>(query => query);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public virtual IEnumerable<TEntity> Query<TEntity>(Func<IQueryable<TEntity>, IQueryable<TEntity>> predicate) where TEntity : class
        {
            return Execute<IEnumerable<TEntity>>(dbContext => predicate.Invoke(Set<TEntity>(dbContext)));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public override IEnumerable<TEntity> Query<TEntity>(Expression<Func<TEntity, bool>> predicate)
        {
            return Query<TEntity>(query => query.Where(predicate));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="predicate"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public virtual PagedResult<TEntity> Query<TEntity>(Func<IQueryable<TEntity>, IQueryable<TEntity>> predicate, int pageIndex, int pageSize) where TEntity : class
        {
            return Execute<PagedResult<TEntity>>(dbContext =>
            {
                IQueryable<TEntity> query = predicate.Invoke(Set<TEntity>(dbContext)).Skip((pageIndex - 1) * pageSize).Take(pageSize);

                return new PagedResult<TEntity>() { PageOfResults = query, TotalRecords = query.Count() };
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="predicate"></param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public override PagedResult<TEntity> Query<TEntity>(Expression<Func<TEntity, bool>> predicate, int pageIndex, int pageSize)
        {
            return Query<TEntity>(query => query.Where(predicate), pageIndex, pageSize);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        public override IEnumerable<TEntity> QueryAll<TEntity>()
        {
            return Query<TEntity>(query => query);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public override PagedResult<TEntity> QueryAll<TEntity>(int pageIndex, int pageSize)
        {
            return Query<TEntity>(query => query, pageIndex, pageSize);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public virtual TEntity QuerySingle<TEntity>(Func<IQueryable<TEntity>, IQueryable<TEntity>> predicate) where TEntity : class
        {
            return Execute(dbContext => predicate.Invoke(Set<TEntity>(dbContext)).SingleOrDefault());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public override TEntity QuerySingle<TEntity>(Expression<Func<TEntity, bool>> predicate)
        {
            return QuerySingle<TEntity>(query => query.Where(predicate));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        public override void Add<TEntity>(TEntity entity)
        {
            Execute(dbContext => Set<TEntity>(dbContext).Add(entity));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="entity"></param>
        public override void Remove<TEntity>(TEntity entity)
        {
            Execute(dbContext => Set<TEntity>(dbContext).Remove(entity));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        public override void Update<TEntity>(TEntity entity)
        {
            //Execute(dbContext => DbSet<TEntity>(dbContext).AddObject(entity));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        public override void Merge<TEntity>(TEntity entity)
        {
            //Execute(dbContext => DbSet<TEntity>(dbContext).AddObject(entity));
        }
    }
}
